package engine;

import java.awt.Cursor;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.MemoryImageSource;
import math.vec2i;

public class Mouse {
    
    private static Cursor cursorDefault;
    private static Cursor cursorInvis;
    
    public static Cursor getDefaultCursor() {
        if (cursorDefault == null) {
            cursorDefault = Cursor.getDefaultCursor();
        }
        return cursorDefault;
    }
    
    public static Cursor getInvisibleCursor() {
        if (cursorInvis == null) {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Image image = toolkit.createImage(new MemoryImageSource(16, 16, new int[16 * 16], 0, 16));
            cursorInvis = toolkit.createCustomCursor(image, new Point(0, 0), "cursorInvis");
        }
        return cursorInvis;
    }
    
    private Robot mouseControl;
    private vec2i componentPos; // Component position, in screen coordinates
    private vec2i componentDim; // Component dimensions, in screen coordinates
    private Point posScreen; // Mouse position, in screen coordinates
    private vec2i posPrevVP; // Previous mouse position, in viewport coordinates
    private vec2i posVP; // Current mouse position, in viewport coordinates
    private vec2i deltaPos;
    private boolean lock; // Is mouse locked to a specific position
    private vec2i posLockVP; // Locked mouse position, int viewport coordinates
    private Cursor cursor;
    
    public Mouse(int viewportW, int viewportH) {
        try {
            mouseControl = new Robot();
        } catch (Exception e) {
            System.out.println(e);
            System.exit(0);
        }
        cursor = getDefaultCursor();
        componentPos = new vec2i();
        componentDim = new vec2i(viewportW, viewportH);
        posScreen = MouseInfo.getPointerInfo().getLocation();
        posPrevVP = new vec2i();
        posVP = new vec2i();
        deltaPos = new vec2i();
        lock = false;
        posLockVP = new vec2i();
    }
    
    public void update() {
        posScreen = MouseInfo.getPointerInfo().getLocation();
        if (!lock) {
            posPrevVP.x = posVP.x;
            posPrevVP.y = posVP.y;
        }
        posVP.x = posScreen.x - componentPos.x;
        posVP.y = posScreen.y - componentPos.y;
        if (posVP.x < 0) {
            posVP.x = 0;
        } else if (posVP.x >= componentDim.x) {
            posVP.x = componentDim.x - 1;
        }
        if (posVP.y < 0) {
            posVP.y = 0;
        } else if (posVP.y >= componentDim.y) {
            posVP.y = componentDim.y - 1;
        }
        if (lock) {
            deltaPos.x = posVP.x - posLockVP.x;
            deltaPos.y = posVP.y - posLockVP.y;
            mouseControl.mouseMove(posLockVP.x + componentPos.x, posLockVP.y + componentPos.y);
        } else {
            deltaPos.x = posVP.x - posPrevVP.x;
            deltaPos.y = posVP.y - posPrevVP.y;
        }
    }
    
    public void lockMouseTo(int x, int y) {
        cursor = getInvisibleCursor();
        posLockVP.x = x;
        posLockVP.y = y;
        lock = true;
        mouseControl.mouseMove(posLockVP.x, posLockVP.y);
    }
    
    public void unlockMouse() {
        cursor = getDefaultCursor();
        posVP.x = posScreen.x - componentPos.x; // Reset posVP
        posVP.y = posScreen.y - componentPos.y;
        posPrevVP.x = posVP.x; // Reset posPrevVP
        posPrevVP.y = posVP.y;
        posLockVP.x = 0;
        posLockVP.y = 0;
        lock = false;
    }
    
    public void setComponentPos(Point componentPosition) {
        componentPos.x = componentPosition.x;
        componentPos.y = componentPosition.y;
    }
    
    public boolean isOutsideViewport() {
        return (posScreen.x < componentPos.x || posScreen.x >= componentPos.x + componentDim.x)
                || (posScreen.y < componentPos.y || posScreen.y >= componentPos.y + componentDim.y);
    }
    
    public boolean isInsideViewport() {
        return !(isOutsideViewport());
    }
    
    public int mouseDX() {
        return deltaPos.x;
    }
    
    public int mouseDY() {
        return deltaPos.y;
    }
    
    public int mouseX() {
        return posVP.x;
    }
    
    public int mouseY() {
        return posVP.y;
    }
    
    public Cursor getCursor() {
        return cursor;
    }

}
